﻿using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace NET.HLW5a.Snippets.Images
{
    public static class Copy
    {
        /// <summary>
        /// constant for quickly accessing the alpha channel
        /// </summary>
        private const Int16 A = 3;
        /// <summary>
        /// constant for quickly accessing the red color channel
        /// </summary>
        private const Int16 R = 2;
        /// <summary>
        /// constant for quickly accessing the green color channel
        /// </summary>
        private const Int16 G = 1;
        /// <summary>
        /// constant for quickly accessing the blue color channel
        /// </summary>
        private const Int16 B = 0;
        
        /// <summary>
        /// Copies the bitmap data from source onto destination
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dst"></param>
        public static unsafe void Copy1(Bitmap src, Bitmap dst)
        {
            if (src.Width != dst.Width || src.Height != dst.Height) throw new ArgumentException(@"Source and destination bitmap must have the same dimensions."); // throw exception or use bitmap scaling

            BitmapData srcData = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadOnly, src.PixelFormat); // pin the source image to memory
            BitmapData dstData = dst.LockBits(new Rectangle(0, 0, dst.Width, dst.Height), ImageLockMode.WriteOnly, dst.PixelFormat); // pin the destination image to memory

            Int32 srcBpp = GetBpp(src.PixelFormat), dstBpp = GetBpp(dst.PixelFormat); // calculate color depths for source and destination
            Int32 srcOffset = srcData.Stride - srcData.Width * srcBpp, dstOffset = dstData.Stride - dstData.Width * dstBpp; // calculate stride offset for source and destination

            Byte* srcPt = (Byte*)srcData.Scan0.ToPointer(), dstPt = (Byte*)dstData.Scan0.ToPointer(); // set pointer to the beginning of the source- and destination-stream

            for (Int32 y = 0; y < src.Height; y++, srcPt += srcOffset, dstPt += dstOffset) // for each line in the image...
            {
                for (Int32 x = 0; x < src.Width; x++, srcPt += srcBpp, dstPt += dstBpp) // for each pixel in the line...
                {
                    if (dstBpp == 4) dstPt[A] = (srcBpp == 4) ? srcPt[A] : Byte.MaxValue; // copy the alpha channel if necessary and  existent
                    dstPt[R] = srcPt[R]; // copy the red color channel
                    dstPt[G] = srcPt[G]; // copy the green color channel
                    dstPt[B] = srcPt[B]; // copy the blue color channel
                }
            }

            src.UnlockBits(srcData); // release the source image
            dst.UnlockBits(dstData); // release the destination image
        }

        private static Int32 GetBpp(PixelFormat pf)
        {
            switch (pf)
            {
                case PixelFormat.Format32bppRgb:
                case PixelFormat.Format32bppArgb:
                case PixelFormat.Format32bppPArgb:
                    return 4;
                case PixelFormat.Format24bppRgb:
                    return 3;
                default:
                    throw new NotSupportedException(String.Format(@"Pixelformat {0} not supported.", pf.ToString()));
            }
        }
    }
}
